{-# LANGUAGE OverloadedStrings #-}
module HaskellCmdInputController
    (
     Controller
     , new
     , onUpdate
     , view
     , OnUpdate
     , runText
     , Env
     ) where

import qualified Graphics.UI.Gtk.ModelView as MV 
import qualified TextInputView as View
import Graphics.UI.Gtk
import WindowedApp
import Component
import qualified Data.ByteString.Lazy.Char8 as L
import Control.Concurrent.MVar
import qualified LoadSaveController as LSC
import Language.Haskell.Interpreter
import GHC.Exts( IsString(..) )
import Control.Concurrent
import Control.Applicative
import qualified Data.Map as M
import qualified Control.Exception as Exc
import Control.Parallel.Strategies
import Data.List

type Controller = Ref C

view = View.mainWidget . gui

new env = do
  buf <- textBufferNew Nothing
  textBufferSetText buf "\\env ->\n  let rows_t1 = env ! \"t1\" in\n  [[\"test\"]] ++ rows_t1"
  lsc <- LSC.new (Just "hs")
  lscv <- (lsc .> LSC.view)
  v@(View.V _ runB _ _ _) <- View.new buf lscv
  lock <- newEmptyMVar
  this <- newRef (C v "" Nothing buf lock env)
  runB `onClicked` (this .<< runText)
  lsc .< (LSC.onLoad (Just (\cont -> textBufferSetText buf (L.unpack cont))))
  lsc .< (LSC.onSave (Just (Just <$> L.pack <$> getBufferText buf)))       
  onBufferChanged buf (lsc .>> LSC.clearLabel)
  return this

type OnUpdate = [[String]] -> IO ()
type Env = M.Map String [[String]]

onUpdate :: Maybe OnUpdate -> C -> C
onUpdate cb state = state {executeCB = cb}
-- internal functions 
data C = C 
    { 
      gui :: View.ViewState
    , currentCmd :: String
    , executeCB :: Maybe OnUpdate
    , buffer :: TextBuffer
    , execLock :: MVar Bool
    , env :: IO Env
    }

getBufferText buf = do
  s <- textBufferGetStartIter buf
  e <- textBufferGetEndIter buf
  textBufferGetText buf s e True
  
runText :: C -> IO C
runText state = do
  locked <- tryPutMVar (execLock state) True
  if locked then
      case executeCB state of
        Just execCB -> 
            do
              text <- getBufferText (buffer state)
              forkOS $ executeCmd text execCB state
              return $ state
        Nothing -> 
            return state
    else return state

executeCmd text execCB state = do
  e <- env state
--  let e = M.insert "t1" [["Hallo"],["welt"]] M.empty
  postGUISync $ widgetSetSensitivity (View.cancelB $ gui state) False
  postGUISync $ widgetSetSensitivity (View.executeB $ gui state) False
  postGUISync $ widgetSetSensitivity (View.textView $ gui state) False
  postGUISync $ labelSetText (View.exitCodeL $ gui state) $ "Executing..."
  let haskellCode = "(" ++ text ++ ") :: (Map String [[String]] -> [[String]])"
  res <- runInterpreter (evalText haskellCode)
  case res of 
    Right func -> do 
      let ex_handler :: Exc.SomeException -> IO [[String]]
          ex_handler exc = postGUISync (labelSetText (View.exitCodeL $ gui state) ("Error: " ++ show exc)) >> return []
      lines <- Exc.catch (let res' = func e
                          in (res' `using` rnf) `seq` do
                            postGUISync $ labelSetText 
                                            (View.exitCodeL $ gui state) 
                                            ("Procuced " ++ show (length res') ++ " rows")
                            return res')
                         ex_handler
      postGUISync (execCB lines)

    Left errs -> 
      postGUISync $ labelSetText (View.exitCodeL $ gui state) ("Error: " ++ show  errs)

  postGUISync $ widgetSetSensitivity (View.cancelB $ gui state) False
  postGUISync $ widgetSetSensitivity (View.textView $ gui state) True
  postGUISync $ widgetSetSensitivity (View.executeB $ gui state) True
  takeMVar (execLock state)
  return ()
 where 
    evalText :: String -> Interpreter (Env -> [[String]])          
    evalText source = do
      setImports ["Data.Map", "Prelude", "Data.List", "Data.Char"]
      interpret source (undefined ::(Env -> [[String]]))
           
-- tests
main = windowedApp "HaskellCmdInputController test" $ do
         t <- new (return M.empty) :: IO Controller
         t .< onUpdate (Just ((mapM_ (putStrLn . show)) 
                              . (take 100)))
         t .> view
         

